#pragma once

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <memory>
#include <functional>


#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using namespace LogMudule;
using namespace ThreadPoolModule;

static const uint16_t gport = 8080;
#define BACKLOG 8

class TcpServer
{
    using task_t = std::function<void()>;
    using handler_t = std::function<std::string(std::string&)>;
    struct ThreadData
    {
        int sockfd;
        TcpServer *self;
    };
public:
    TcpServer(handler_t handler,int port = gport)
        :_handler(handler)
        ,_port(port)
    {
    }

    void InitServer()
    {
        // 1. create socket
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket是SOCK_STREAM
        if (_listensockfd < 0)
        {
            LOG(LogLevel::ERROR) << "create socket failed";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success, Socketfd = " << _listensockfd;

        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        addr.sin_addr.s_addr = INADDR_ANY;

        // 2. bind socket
        int n = ::bind(_listensockfd, CONV(&addr), sizeof(addr));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socket failed";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind socket success";

        // 3. listen socket
        // cs模式 客户端与服务端，就要求tcp随时随地等待被连接,tcp 需要将socket设置为监听模式，等待客户端的连接
        // 3.1 listen
        n = ::listen(_listensockfd, BACKLOG);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen socket failed";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen socket success";

        // 4. 设置非阻塞
        ::signal(SIGCHLD, SIG_IGN); // 子进程退出，OS会自动回收资源，不需要wait了
    }


    // Tcp也是全双工
    void HandlerRequest(int sockfd)
    {
        LOG(LogLevel::INFO) << "HandlerRequest, sockfd is : " << sockfd;
        char inbuffer[4096];
        std::string package;
        // 长任务
        while (true)
        {
            // 约定 : 用户发过来的是一个完整的命令string
            // ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);
            ssize_t n = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0); // recv读取是不完善的
            if (n > 0)
            {
                inbuffer[n] = 0;
                LOG(LogLevel::INFO) << "\n" <<inbuffer;
                package += inbuffer;

                std::string cmd_result = _handler(package);
                if(cmd_result.empty()) continue; // TODO

                send(sockfd, cmd_result.c_str(), cmd_result.size(), 0);
            }
            else if (n == 0)
            {
                // read 如果读取返回值是0，表示client退出
                LOG(LogLevel::INFO) << "client quit: " << sockfd;
                break;
            }
        }
        ::close(sockfd);
    }


    static void *ThreadEntry(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *data = (ThreadData *)args;
        data->self->HandlerRequest(data->sockfd);
        delete data;
        return nullptr;
    }


    void Start()
    {
        _isrunning = true;
        LOG(LogLevel::INFO) << "accepting......";
        while (_isrunning)
        {
            // 不能直接读取数据
            // 1. 获取新连接
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error" << strerror(errno);
                continue;
            }

            // 2. 获取连接成功了
            LOG(LogLevel::INFO) << "accept a new connection, sockfd = " << sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "client info" << addr.Addr();


            // 3. 处理连接
            // // v-1 多进程模式
            task_t task = std::bind(&TcpServer::HandlerRequest, this, sockfd);
            ThreadPool<task_t>::getInstance()->Equeue(task);
        }
    }


    void Stop()
    {
        _isrunning = false;
    }

    ~TcpServer()
    {
    }

private:
    int _listensockfd; // 监听套接字
    uint16_t _port;
    bool _isrunning;

    // 处理上层任务
    handler_t _handler;
};